package com.aliang.shopping.controller;

import cn.hutool.captcha.LineCaptcha;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.aliang.shopping.common.Result;
import com.aliang.shopping.common.ResultCodeEnum;
import com.aliang.shopping.config.OssConfig;
import com.aliang.shopping.contact.BasicConstant;
import com.aliang.shopping.contact.ResultMsgContact;
import com.aliang.shopping.contact.SqlColumnConstant;
import com.aliang.shopping.exception.DataException;
import com.aliang.shopping.exception.SecurityException;
import com.aliang.shopping.exception.ServiceException;
import com.aliang.shopping.log.annotation.LogLogin;
import com.aliang.shopping.log.annotation.OperateLog;
import com.aliang.shopping.log.enums.LoginType;
import com.aliang.shopping.log.enums.OperateType;
import com.aliang.shopping.model.dto.login.EmailLoginDTO;
import com.aliang.shopping.model.dto.login.LoginDTO;
import com.aliang.shopping.model.dto.login.PhoneLoginDTO;
import com.aliang.shopping.model.enums.UserTypeEnum;
import com.aliang.shopping.model.po.Business;
import com.aliang.shopping.model.po.UserInfo;
import com.aliang.shopping.model.vo.CaptchaVO;
import com.aliang.shopping.model.vo.CommonUserInfoVO;
import com.aliang.shopping.security.PreventRepeatSubmit;
import com.aliang.shopping.security.auth.Payload;
import com.aliang.shopping.security.auth.UserHolder;
import com.aliang.shopping.service.AdminService;
import com.aliang.shopping.service.BusinessService;
import com.aliang.shopping.service.UserInfoService;
import com.aliang.shopping.util.CodeUtil;
import com.aliang.shopping.util.JwtUtil;
import com.aliang.shopping.util.MailUtil;
import com.aliang.shopping.util.RequestUtil;
import com.aliang.shopping.util.ali.SMSTemplate;
import com.aliyuncs.exceptions.ClientException;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.time.Duration;
import java.util.Objects;

/**
 * 基础控制器
 */
@RestController
@RequestMapping("/basic")
@AllArgsConstructor
public class BasicController {

    private final RedisTemplate<String, String> redisTemplate;
    private final SMSTemplate smsTemplate;
    private final AdminService adminService;
    private final BusinessService businessService;
    private final UserInfoService userInfoService;

    /**
     * 获取图片验证码
     *
     * @param timeLimitFlag 时间限制标志
     * @return
     */
    @ApiOperation("获取图片验证码")
    @GetMapping("/getCode/{timeLimitFlag}")
    public Result<CaptchaVO> getCode(@PathVariable Boolean timeLimitFlag) {
        //随机生成算术和随机数验证码
        long rand = System.currentTimeMillis() % 2;
        String code;
        String imageData;
        //随机数
        if (rand == 0) {
            LineCaptcha randomNUmber = CodeUtil.getRandomNUmber();
            code = randomNUmber.getCode();
            imageData = randomNUmber.getImageBase64Data();
        } else {
            //四则运算
            LineCaptcha mathCode = CodeUtil.getMathCode();
            //获取运算结果
            code = CodeUtil.getMachResult(mathCode.getCode());
            imageData = mathCode.getImageBase64Data();
        }
        CaptchaVO captcha;
        //判断是否有时间限制，
        if (timeLimitFlag) {
            //有时间限制由后端判断是否一致，生成键存入redis并返回键
            //生成键
            String key = UUID.randomUUID().toString();
            redisTemplate.opsForValue().set(BasicConstant.IMAGE_LOGIN_KEY + key, Objects.requireNonNull(code), Duration.ofMinutes(10));
            captcha = CaptchaVO.builder().captchaKey(key)
                    .captchaBase64(imageData)
                    .build();
        } else {
            //没有时间限制，把验证码返回给前端，由前端验证
            captcha = CaptchaVO.builder().captchaKey(code)
                    .captchaBase64(imageData)
                    .build();
        }
        return Result.ok(captcha);
    }

    private final RequestUtil requestUtil;

    @ApiOperation("发送手机验证码")
    @GetMapping("/send_phone_code/{phone}")
    @OperateLog(title = "发送手机验证码", type = OperateType.OTHER)
    @PreventRepeatSubmit
    public Result<String> sendPhoneCode(@PathVariable String phone, @RequestParam(required = false, defaultValue = "false") Boolean isRegister, HttpServletRequest request) {
        //验证手机格式
        if (StrUtil.isBlank(phone) || !Validator.isMobile(phone)) {
            throw new DataException(ResultCodeEnum.PHONE_NOT_LEGAL);
        }
        //判断手机是否发送验证码
        Boolean hasKey = redisTemplate.hasKey(BasicConstant.PHONE_LOGIN_KEY + phone);
        if (Boolean.TRUE.equals(hasKey)) {
            throw new DataException(ResultCodeEnum.PHONE_CODE_IS_SEND);
        }
        // 获取当前用户的ip
        String ip = requestUtil.getClientIP(request);
        // 将.转换成:，方便Redis分组
        ip = ip.replace(".", ":");
        // 受资源限制，每个用户每天只能发10条验证码，具体可以根据配置设置
        // 读取该ip的发送次数
        String countStr = redisTemplate.opsForValue().get(BasicConstant.SHOPPING_SEND_PHONE_KEY + ip);
        if (Objects.nonNull(countStr)) {
            // 获取最大发送次数
            Integer maxCount = smsTemplate.getMaxCount();
            int count = Integer.parseInt(countStr);
            if (count >= maxCount) {
                throw new DataException(ResultCodeEnum.PHONE_CODE_SEND_OVER);
            }
            // 如果发送次数小于最大次数，则发送次数加1
            redisTemplate.opsForValue().set(BasicConstant.SHOPPING_SEND_PHONE_KEY + ip, String.valueOf(count + 1));
        } else {
            // 为空，记录发送次数
            redisTemplate.opsForValue().set(BasicConstant.SHOPPING_SEND_PHONE_KEY + ip, "1");
        }
        // 如果不是注册电话，需要判断电话是否已注册，未注册的不能发送验证码
        if (!isRegister) {
            int count = this.userInfoService.countWithPhone(phone);
            if (count == 0) {
                throw new DataException(ResultCodeEnum.PHONE_NOT_EXIT);
            }
        }
        //生成4位验证码
        String code = RandomUtil.randomNumbers(6);
        //发送验证码
        try {
            String message = smsTemplate.send(phone, code);
            if (Objects.equals(message, "OK")) {
                //发送成功,存入redis,5分钟
                redisTemplate.opsForValue().set(BasicConstant.PHONE_LOGIN_KEY + phone, code, Duration.ofMinutes(5));
                return Result.ok(ResultMsgContact.PHONE_CODE_SEND_SUCCESS);
            }
            //发送失败，抛出业务异常
            throw new ServiceException(message);
        } catch (ClientException e) {
            throw new ServiceException(ResultCodeEnum.PHONE_CODE_SEND_ERROR);
        }
    }

    private final MailUtil mailUtil;

    @ApiOperation("发送邮箱验证码")
    @GetMapping("/send_email_code")
    @OperateLog(title = "发送邮箱验证码", type = OperateType.OTHER)
    @PreventRepeatSubmit
    public Result<String> sendEmailCode(String email, @RequestParam(required = false, defaultValue = "false") Boolean isRegister) {
        //验证邮箱格式
        if (!Validator.isEmail(email)) {
            throw new DataException(ResultCodeEnum.EMAIL_NOT_LEGAL);
        }
        //判断邮箱是否发送验证码
        if (Boolean.TRUE.equals(redisTemplate.hasKey(BasicConstant.EMAIL_LOGIN_KEY + email))) {
            throw new DataException(ResultCodeEnum.EMAIL_CODE_IS_SEND);
        }
        // 如果不是注册邮箱，需要判断邮箱是否已注册，未注册的不能发送验证码
        if (!isRegister) {
            int count = this.userInfoService.countWithEmail(email);
            if (count == 0) {
                throw new DataException(ResultCodeEnum.EMAIL_NOT_LEGAL);
            }
        }
        //生成8位验证码
        String code = RandomUtil.randomNumbers(6);
        // 发送验证码
        this.mailUtil.sendCode(email, code);
        //发送成功，存入redis，5分钟
        redisTemplate.opsForValue().set(BasicConstant.EMAIL_LOGIN_KEY + email, code, Duration.ofMinutes(5));
        return Result.ok(ResultMsgContact.EMAIL_CODE_SEND_SUCCESS);
    }

    @ApiOperation("发送验证邮箱")
    @GetMapping("/send_validate_email")
    @OperateLog(title = "发送验证邮箱", type = OperateType.OTHER)
    public Result<String> sendValidateEmail(String email) {
        //验证邮箱格式
        if (!Validator.isEmail(email)) {
            throw new DataException(ResultCodeEnum.EMAIL_NOT_LEGAL);
        }
        //发送验证邮箱
        this.mailUtil.sendValidateEmail(email);
        return Result.ok(ResultMsgContact.EMAIL_VALIDATE_SEND_SUCCESS);
    }

    @ApiOperation("登录")
    @PostMapping("/login")
    @LogLogin(loginType = LoginType.ACCOUNT)
    public Result<String> login(@RequestBody LoginDTO loginDTO) {
        //非空校验
        if (BeanUtil.hasNullField(loginDTO)) {
            throw new DataException(ResultCodeEnum.DATA_LACK);
        }
        //密码长度校验
        if (StrUtil.length(loginDTO.getPassword()) < 8) {
            throw new DataException(ResultCodeEnum.PASSWORD_LENGTH_ERR);
        }
        //验证码校验
        String code = redisTemplate.opsForValue().get(BasicConstant.IMAGE_LOGIN_KEY + loginDTO.getCaptchaKey());
        validateCode(loginDTO.getCaptchaCode(), code);
        Payload payload;
        //登录验证
        if (Objects.equals(loginDTO.getUserType(), UserTypeEnum.USER.getCode())) {
            payload = this.userInfoService.login(loginDTO);
        } else if (Objects.equals(loginDTO.getUserType(), UserTypeEnum.BUSINESS.getCode())) {
            payload = this.businessService.login(loginDTO);
        } else {
            payload = this.adminService.login(loginDTO);
        }
        //验证通过，删除验证码
        redisTemplate.delete(BasicConstant.IMAGE_LOGIN_KEY + loginDTO.getCaptchaKey());
        //生成token
        return Result.ok(JwtUtil.getToken(payload));
    }

    @ApiOperation("邮箱验证登录")
    @PostMapping("/email_login")
    @LogLogin(loginType = LoginType.EMAIL)
    public Result<String> emailLogin(@RequestBody EmailLoginDTO loginDTO) {
        //非空校验
        if (StrUtil.hasBlank(loginDTO.getEmail(), loginDTO.getCaptchaCode())) {
            throw new DataException(ResultCodeEnum.DATA_LACK);
        }
        //邮箱格式验证
        if (!Validator.isEmail(loginDTO.getEmail())) {
            throw new DataException(ResultCodeEnum.EMAIL_NOT_LEGAL);
        }
        //验证码校验
        String code = redisTemplate.opsForValue().get(BasicConstant.EMAIL_LOGIN_KEY + loginDTO.getEmail());
        validateCode(loginDTO.getCaptchaCode(), code);

        //根据邮箱获取用户信息
        Payload payload;
        payload = this.userInfoService.emailLogin(loginDTO);
        if (ObjUtil.isNotEmpty(payload)) {
            return Result.ok(JwtUtil.getToken(payload));
        }
        // 商家登录
        payload = this.businessService.emailLogin(loginDTO);
        if (ObjUtil.isNotEmpty(payload)) {
            return Result.ok(JwtUtil.getToken(payload));
        }
        // 管理员登录
        payload = this.adminService.emailLogin(loginDTO);
        if (ObjUtil.isNotEmpty(payload)) {
            return Result.ok(JwtUtil.getToken(payload));
        }
        //已经被消费，删除键
        redisTemplate.delete(BasicConstant.EMAIL_LOGIN_KEY + loginDTO.getEmail());
        throw new ServiceException(ResultCodeEnum.EMAIL_NOT_EXIT);
    }

    private static void validateCode(String sourceCode, String code) {
        if (!StrUtil.equals(sourceCode, code)) {
            throw new DataException(ResultCodeEnum.CAPTCHA_NOT_MATCH);
        }
    }

    @ApiOperation("手机验证登录")
    @PostMapping("/phone_login")
    @LogLogin(loginType = LoginType.PHONE)
    public Result<String> phoneLogin(@RequestBody PhoneLoginDTO loginDTO) {
        //非空校验
        if (StrUtil.hasBlank(loginDTO.getPhone(), loginDTO.getCaptchaCode())) {
            throw new DataException(ResultCodeEnum.DATA_LACK);
        }
        //手机格式验证
        if (!Validator.isMobile(loginDTO.getPhone())) {
            throw new DataException(ResultCodeEnum.PHONE_NOT_LEGAL);
        }
        //验证码校验
        String code = redisTemplate.opsForValue().get(BasicConstant.PHONE_LOGIN_KEY + loginDTO.getPhone());
        validateCode(loginDTO.getCaptchaCode(), code);

        Payload payload;
        //根据电话获取用户信息
        // 用户登录
        payload = this.userInfoService.phoneLogin(loginDTO);
        if (ObjUtil.isNotEmpty(payload)) {
            return Result.ok(JwtUtil.getToken(payload));
        }
        // 商家登录
        payload = this.businessService.phoneLogin(loginDTO);
        if (ObjUtil.isNotEmpty(payload)) {
            return Result.ok(JwtUtil.getToken(payload));
        }  // 管理员登录
        payload = this.adminService.phoneLogin(loginDTO);
        if (ObjUtil.isNotEmpty(payload)) {
            return Result.ok(JwtUtil.getToken(payload));
        }
        //已经被消费，删除键
        redisTemplate.delete(BasicConstant.PHONE_LOGIN_KEY + loginDTO.getPhone());
        throw new ServiceException(ResultCodeEnum.PHONE_NOT_EXIT);
    }

    /**
     * 判断账号是否唯一
     */
    @PostMapping("/validate_username/{type}")
    @ApiOperation("判断账号是否唯一")
    public Result<String> validateBusinessUsername(@PathVariable Boolean type, String username) {
        // 判断账号唯一,type为true为商家账号
        int count = type ? businessService.count(new QueryWrapper<Business>().eq(SqlColumnConstant.USERNAME, username))
                : userInfoService.count(new QueryWrapper<UserInfo>().eq(SqlColumnConstant.USERNAME, username));
        return count > 0 ? Result.fail(ResultMsgContact.USERNAME_IS_EXIT) : Result.ok("");
    }

    private final OssConfig ossConfig;

    @ApiOperation("获取登录用户的基本信息")
    @GetMapping("/get_user_info")
    public Result<CommonUserInfoVO> getUserInfo() {
        //获取当前登录用户的信息
        Payload payload = UserHolder.get();
        if (ObjectUtil.isEmpty(payload)) {
            // 没有登录，返回错误
            throw new SecurityException(ResultCodeEnum.USER_NOT_LOGIN);
        }
        Long id = payload.getId();
        Integer role = payload.getRole();
        CommonUserInfoVO commonUserInfoVO = new CommonUserInfoVO();
        if (Objects.equals(role, UserTypeEnum.USER.getCode())) {
            commonUserInfoVO = this.userInfoService.getUserInfo(id);
            commonUserInfoVO.setRole(role);
        } else if (Objects.equals(role, UserTypeEnum.BUSINESS.getCode())) {
            commonUserInfoVO = this.businessService.getUserInfo(id);
            commonUserInfoVO.setRole(role);
        } else {
            //获取管理员的信息
            commonUserInfoVO = this.adminService.getUserInfo(id);
        }
        // 设置完整图片路径
        if (StrUtil.isNotBlank(commonUserInfoVO.getAvatar()))
            commonUserInfoVO.setAvatar(ossConfig.getUrl() + commonUserInfoVO.getAvatar());
        return Result.ok(commonUserInfoVO);
    }

    @ApiOperation("登录测试")
    @PostMapping("/login_test")
    public Result<String> loginTest() {
        Payload payload = new Payload(1862520647094738946L, UserTypeEnum.USER.getCode());
        //生成token
        return Result.ok(JwtUtil.getToken(payload));
    }

}
